/* Orbas:
 *     A open source CORBA Specification implementation from Huihoo.
 *
 * Copyright 2002-2003 Huihoo.org, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */

package org.huihoo.orbas.orb.ior;

/**
 * <p>Description: </p>
 * IOR parser.
 * <p>Copyright (c) 2002,2003</p>
 * <p>Company: <a href="http://www.huihoo.org/">huihoo.org</a></p>
 * @author <a href="http://www.huihoo.org/~mep">mep(mep@huihoo.com)</a>
 * @see <a href="http://www.huihoo.org/orbas">http://www.huihoo.org/orbas</a>
 * @version 1.0
 */

import org.huihoo.orbas.orb.ObjRefDelegate;
import org.huihoo.orbas.orb.ObjectImpl;
import org.huihoo.orbas.orb.cdr.CDRInputStream;
import org.huihoo.orbas.orb.cdr.CDROutputStream;
import org.huihoo.orbas.orb.protocols.ProfileExtractor;
import org.huihoo.orbas.orb.protocols.ProfileRegistry;
import org.huihoo.orbas.orb.protocols.TargetAddress;
import org.omg.IOP.TaggedProfile;
import org.omg.IOP.TaggedProfileHelper;

public class IorParser {

	private org.huihoo.orbas.orb.ORB orb;
	private TargetAddress target;
	private TaggedProfile[] profiles;
	private String typeId;
	private String s;
	private byte[] ior;
	private byte[] objectKey;
	private org.omg.CORBA.Object obj;
	private int version = 0;

	public IorParser() {
		orb = null;
	}

	public IorParser(org.huihoo.orbas.orb.ORB orb, byte[] ior) {
		this.ior = ior;
		this.orb = orb;

	}

	public IorParser(org.huihoo.orbas.orb.ORB orb, CDRInputStream in) {
		this.orb = orb;
		this.obj = parseObject(in);

	}

	public IorParser(org.huihoo.orbas.orb.ORB orb, String s) {
		this.orb = orb;
		this.s = s;
		this.obj = string2Object(s);
	}

	public IorParser(org.huihoo.orbas.orb.ORB orb, TaggedProfile[] profiles,
			String typeId) {
		this.orb = orb;
		this.profiles = profiles;
		this.typeId = typeId;

	}

	protected org.omg.CORBA.Object parseObject(CDRInputStream in) {
		byte byteOrder = in.read_octet();
		if (byteOrder == 1)
			in.setLittleEndian(true);
		typeId = in.read_string();
		parseTaggedProfiles(in);
		ObjRefDelegate delegate = new ObjRefDelegate(this.orb, this);
		ObjectImpl objectImpl = new ObjectImpl();
		objectImpl._set_delegate(delegate);
		return objectImpl;
	}

	protected org.omg.CORBA.Object string2Object(String s) {
		java.util.StringTokenizer tok = new java.util.StringTokenizer(s, ":");
		tok.nextToken();
		String iorContent = tok.nextToken();
		CDRInputStream in = new CDRInputStream(orb, string2Cdr(iorContent));

		byte byteOrder = in.read_octet();
		if (byteOrder == 1) {
			in.setLittleEndian(true);
		}

		typeId = in.read_string();
		parseTaggedProfiles(in);
		ObjRefDelegate delegate = new ObjRefDelegate(this.orb, this);
		ObjectImpl objectImpl = new ObjectImpl();
		objectImpl._set_delegate(delegate);
		return objectImpl;
	}

	protected void parseTaggedProfiles(CDRInputStream in) {
		int nProfiles = in.read_long();
		profiles = new TaggedProfile[nProfiles];

		for (int i = 0; i < nProfiles; i++) {

			profiles[i] = parseSingleProfile(in);

			// read tag from config file to select supported protocols
			if (profiles[i].tag == org.omg.IOP.TAG_INTERNET_IOP.value) {
				// IIOP profile,select support ProfileExactor

				ProfileRegistry profiledealers = orb.getProfileRegistry();
				ProfileExtractor extractor = profiledealers
						.getProfileExtractor(org.omg.IOP.TAG_INTERNET_IOP.value);

				if (extractor == null) {
					// we cannot deal with this protocols
					System.out
							.println("Error : Transport protocol NOT supported.");
				}

				CDRInputStream profileBodyIn = new CDRInputStream(
						profiles[i].profile_data);

				byte byteOrder = profileBodyIn.read_octet();
				if (byteOrder == 1) {
					profileBodyIn.setLittleEndian(true);
				} else {
					profileBodyIn.setLittleEndian(false);
				}

				extractor.setInputStream(profileBodyIn);
				extractor.parse();
				target = extractor.getTargetAddress();
				objectKey = extractor.getObjectKey();
				version = extractor.getVersion();
			} else if (profiles[i].tag == org.omg.IOP.TAG_MULTIPLE_COMPONENTS.value) {

				// do the same thing?
				ProfileRegistry profiledealers = orb.getProfileRegistry();
				ProfileExtractor extractor = profiledealers
						.getProfileExtractor(org.omg.IOP.TAG_MULTIPLE_COMPONENTS.value);

				if (extractor == null) {
					// we cannot deal with this protocols
					System.out
							.println("Error : Transport protocol NOT supported.");
				}

				extractor.parse();
				target = extractor.getTargetAddress();
				objectKey = extractor.getObjectKey();
			}

		}

	}

	protected TaggedProfile parseSingleProfile(CDRInputStream in) {
		TaggedProfile profile;

		profile = TaggedProfileHelper.read(in);

		return profile;

	}

	public byte[] writeTaggedProfiles() {
		CDROutputStream out = new CDROutputStream(orb);

		out.write_boolean(false);
		out.write_string(typeId);

		out.write_long(profiles.length);
		for (int i = 0; i < profiles.length; i++) {
			TaggedProfile p = profiles[i];
			TaggedProfileHelper.write(out, p);

		}

		ior = new byte[out.getLength() + 1];
		System.arraycopy(out.getBuffer(), 0, ior, 0, out.getLength());

		return ior;
	}

	public String object2String() {

		return cdr2String(ior, ior.length);
	}

	protected String cdr2String(byte[] b, int bLen) {
		int resultLen = 4 + 2 * b.length;
		StringBuffer result = new StringBuffer(resultLen);
		result.append('I');
		result.append('O');
		result.append('R');
		result.append(':');
		for (int j = 0; j < bLen; j++) {
			byte c = b[j];
			result.append(int2Hex((c >> 4) & 0xF));
			result.append(int2Hex(c & 0xF));
		}
		return result.toString();
	}

	protected char int2Hex(int i) {
		switch (i) {
		case 0:
			return '0';
		case 1:
			return '1';
		case 2:
			return '2';
		case 3:
			return '3';
		case 4:
			return '4';
		case 5:
			return '5';
		case 6:
			return '6';
		case 7:
			return '7';
		case 8:
			return '8';
		case 9:
			return '9';
		case 10:
			return 'A';
		case 11:
			return 'B';
		case 12:
			return 'C';
		case 13:
			return 'D';
		case 14:
			return 'E';
		case 15:
			return 'F';
		default:
			// ?
			return '0';
		}
	}

	protected byte[] string2Cdr(String s) {
		int src = 0;
		int dst = 0;
		int sLen = s.length();
		int resultLen = sLen / 2;
		byte[] result = new byte[resultLen];
		while (src < sLen) {
			char first = s.charAt(src++);
			char second = s.charAt(src++);
			byte combined = (byte) (((hex2Int(first) & 0xF) << 4) | (hex2Int(second) & 0xF));
			result[dst++] = combined;
		}
		return result;
	}

	protected int hex2Int(char c) {
		switch (c) {
		case '0':
			return 0;
		case '1':
			return 1;
		case '2':
			return 2;
		case '3':
			return 3;
		case '4':
			return 4;
		case '5':
			return 5;
		case '6':
			return 6;
		case '7':
			return 7;
		case '8':
			return 8;
		case '9':
			return 9;
		case 'A':
		case 'a':
			return 10;
		case 'B':
		case 'b':
			return 11;
		case 'C':
		case 'c':
			return 12;
		case 'D':
		case 'd':
			return 13;
		case 'E':
		case 'e':
			return 14;
		case 'F':
		case 'f':
			return 15;
		default:
			// ?
			return 0;
		}
	}

	public String getTypeId() {
		return typeId;
	}

	public TaggedProfile[] getProfiles() {
		return profiles;
	}

	public org.omg.CORBA.Object getObject() {
		if (obj == null) {
			ObjRefDelegate delegate = new ObjRefDelegate(this.orb, this);
			ObjectImpl objectImpl = new ObjectImpl();
			objectImpl._set_delegate(delegate);
			return objectImpl;
		}

		return obj;
	}

	public byte[] getBytes() {
		if (ior == null) {
			writeTaggedProfiles();
		}
		return ior;
	}

	public String toString() {
		if (ior == null) {
			writeTaggedProfiles();
		}
		if (s == null) {
			s = object2String();
		}
		return s;
	}

	public boolean isA(String Id) {
		return (typeId.equals(Id));
	}

	public byte[] getObjectKey() {
		if (objectKey == null) {

		}

		return objectKey;
	}

	public TargetAddress getAddress() {
		return target;
	}

	public int getVersion() {
		return version;
	}

}
